import logging

from django.db import transaction
from django.db.models import Q, F, Sum
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response

from BYD_ERP.utils.base_views import MultipleDestroyMixin, MultipleAuditMixin
from BYD_ERP.utils.pagination import GlobalPagination
from erp_system.models import UserModel
from basic_info.models import SupplierModel, SettlementAccountModel
from financial_info.models import PaymentModel, PaymentItemModel
from financial_info.serializer.payment_serializer import PaymentSerializer
from warehouse_info.models import InStorageModel

logger = logging.getLogger('my')


class PaymentViewSet(viewsets.ModelViewSet, MultipleDestroyMixin, MultipleAuditMixin):
    """
        create:
        付款单--新增，注意：其中images_list="1,2,3,4"；里面是附件的ID

        付款单新增，status：201（成功），return：新增付款单信息

        destroy:
        付款单--删除

        付款单删除，status：204（成功），return：None

        multiple_delete:
        付款单--批量删除，必传参数：ids=[1,2,3,4,...]

        付款单批量删除，status：204（成功），return：None

        update:
        付款单--修改，注意：其中images_list="1,2,3,4"；里面是附件的ID

        付款单修改，status：200（成功），return：修改后的付款单信息

        partial_update:
        付款单--局部修改，可以传任意属性的值，服务器会修改指定的属性值

        付款单局部修改，status：200（成功），return：修改后的付款单信息

        list:
        付款单列表

        付款单列表信息，status：200（成功），return：付款单列表

        retrieve:
        根据ID查询某一个付款单

        查询指定ID的付款单，status：200（成功），return：指定付款单

    """
    queryset = PaymentModel.objects.all()
    serializer_class = PaymentSerializer
    pagination_class = GlobalPagination

    def get_queryset(self):
        if self.action == 'find':  # 过滤查询
            # 获取请求参数（在json中）：
            number_code = self.request.data.get('number_code', None)  # 单据编号
            check_fin_user = self.request.data.get('check_fin_user', None)
            check_gen_user = self.request.data.get('check_gen_user', None)
            keyword = self.request.data.get('keyword', None)
            start_date = self.request.data.get('start_date', None)
            end_date = self.request.data.get('end_date', None)
            supplier = self.request.data.get('supplier', 0)
            operator_user = self.request.data.get('operator_user', 0)
            status = self.request.data.get('status', 0)  # 状态，0：未审核，1：已审核

            remark = self.request.data.get('remark', None)
            account = self.request.data.get('account', 0)
            purchase_order_number_code = self.request.data.get('purchase_order_number_code', None)   # 关联的采购订单编号

            query = Q()
            if number_code:
                query.add(Q(number_code__contains=number_code), 'AND')  # 单据编号
            if keyword:
                child_query = Q()
                child_query.add(Q(item_list__name__contains=keyword), 'OR')
                child_query.add(Q(item_list__specification__contains=keyword), 'OR')
                child_query.add(Q(item_list__model_number__contains=keyword), 'OR')
                query.add(child_query, 'AND')
            if check_fin_user:
                query.add(Q(check_fin_user__id=check_fin_user), 'AND')
            if check_gen_user:
                query.add(Q(check_gen_user__id=check_gen_user), 'AND')
            if start_date:
                query.add(Q(invoices_date__gt=start_date), 'AND')
            if end_date:
                query.add(Q(invoices_date__lt=end_date), 'AND')
            if supplier:
                query.add(Q(supplier__id__contains=supplier), 'AND')
            if operator_user:
                query.add(Q(operator_user=operator_user), 'AND')
            if status:
                query.add(Q(status=status), 'AND')
            if remark:
                query.add(Q(remark__contains=remark), 'AND')
            if account:
                query.add(Q(account__id=account), 'AND')
            if purchase_order_number_code:
                query.add(Q(purchase_order_number_code__contains=purchase_order_number_code), 'AND')  # 关联的采购订单编号

            # distinct()做去重。
            return PaymentModel.objects.filter(query).distinct().all()
        else:
            return PaymentModel.objects.all()

    params = openapi.Schema(type=openapi.TYPE_OBJECT, properties={
        'keyword': openapi.Schema(type=openapi.TYPE_STRING, description="货品名称的关键字或件号、型号"),
        'check_fin_user': openapi.Schema(type=openapi.TYPE_STRING, description="财务审核"),
        'check_gen_user': openapi.Schema(type=openapi.TYPE_STRING, description="总经理审核"),
        'start_date': openapi.Schema(type=openapi.TYPE_STRING, description="起始日期2024-2-24"),
        'end_date': openapi.Schema(type=openapi.TYPE_STRING, description="结束日期2024-2-24"),
        'status': openapi.Schema(type=openapi.TYPE_STRING, description="状态0或者1"),
        'supplier': openapi.Schema(type=openapi.TYPE_INTEGER, description="供应商的ID"),
        'operator_user': openapi.Schema(type=openapi.TYPE_INTEGER, description="制单人/操作用户的ID"),
        'number_code': openapi.Schema(type=openapi.TYPE_STRING, description="单据编号"),
        'remark': openapi.Schema(type=openapi.TYPE_STRING, description="备注/摘要"),

        'account': openapi.Schema(type=openapi.TYPE_INTEGER, description="结算账户的ID"),
        'purchase_order_number_code': openapi.Schema(type=openapi.TYPE_STRING, description="采购订单号"),
    })

    # 分页参数必须是query_param
    page_param = openapi.Parameter(name='page', in_=openapi.IN_QUERY, description="页号", type=openapi.TYPE_STRING)
    size_param = openapi.Parameter(name='page_size_user_defined', in_=openapi.IN_QUERY, description="每页显示的数量",
                                   type=openapi.TYPE_STRING)

    @swagger_auto_schema(method='post', request_body=params, manual_parameters=[page_param, size_param],
                         operation_description="付款单的过滤查询")
    # swagger_auto_schema中如果method='get'， 是不能用request_body=open_ids的
    # request_body就是定义json格式的传参
    @action(methods=['post'], detail=False)
    def find(self, request, *args, **kwargs):
        return super(PaymentViewSet, self).list(request=request, *args, **kwargs)

    # 付款单的状态，0：未审核，1：已审核
    # 采购入库单的状态，0：未审核，1：已审核，2：部分付款，3：付款完成（包含欠款）
    # 采购订单的状态/采购单的状态，0：未审核，1：已审核，2：部分入库，3：全部入库，4：已付定金，5：完成采购（货全部入库，付全款完成）

    # 教程的模式：
    #   1.付款单没有反审核，因为付出去的款收不回来啦
    #   2.付款和入库的关联关系
    #        除了定金之外，必须是先入库再付款，付款单审核的前提条件包含：入库单审核完毕。

    body_json = openapi.Schema(type=openapi.TYPE_OBJECT, required=['ids', 'user_id', 'is_audit'], properties={
        'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                              description="选择那些需要审核/反审核的ID（主键）列表"),
        'user_id': openapi.Schema(type=openapi.TYPE_INTEGER, description="审核人/审核用户的ID"),
        'is_audit': openapi.Schema(type=openapi.TYPE_STRING, description="是否审核，审核：1，反审核：0"),
    })

    @swagger_auto_schema(method='put', request_body=body_json, operation_description="批量审核/反审核")
    @action(methods=['put'], detail=False)
    @transaction.atomic  # 自动数据库事务，就是说同时执行成功，或者同时不执行成功
    def multiple_audit(self, request, *args, **kwargs):
        audit_ids = request.data.get('ids')
        user_id = request.data.get('user_id')
        is_audit = request.data.get('is_audit', '1')

        if not audit_ids:
            return Response(data={'detail': '参数错误，ids为必传参数'}, status=status.HTTP_400_BAD_REQUEST)
        if not isinstance(audit_ids, list):
            # 如果传过来的参数不是列表
            return Response(data={'detail': '参数错误，ids必须是列表类型'}, status=status.HTTP_400_BAD_REQUEST)

        queryset = self.get_queryset()
        # self就是当前这个对象
        # 所有需要批量审核的付款单列表
        aud_pay_list = queryset.filter(id__in=audit_ids).all()

        if aud_pay_list.count() != len(audit_ids):
            return Response(data={'detail': '审核的数据不存在'}, status=status.HTTP_400_BAD_REQUEST)
        # 加上all()才能变成列表
        for item in aud_pay_list:  # item代表一个单据（付款单）
            if item.status != '1' and is_audit == '0':
                # 付款单状态只有为1的时候才能进行反审核操作
                return Response(data={'detail': '不能进行反审核操作，因为当前单据是未审核状态或已经生效'}, status=status.HTTP_400_BAD_REQUEST)
            if item.status != '0' and is_audit == '1':
                # 付款单状态只有为0的时候才能进行审核操作
                return Response(data={'detail': '不能进行审核操作，因为当前单据已审核过'}, status=status.HTTP_400_BAD_REQUEST)
        # 用户的ID
        if not user_id:
            return Response(data={'detail': '参数错误，user_id为必传参数'}, status=status.HTTP_400_BAD_REQUEST)

        # 审批用户
        check_user = UserModel.objects.get(id=int(user_id))

        # aud_pay_list待审核的付款单列表
        # paym待审核的一条付款单
        for paym in aud_pay_list:

            # 一、付款单进行审核的前提条件，就是说什么情况下才能进行审核。

            #    判断1：付款单状态status必须是0，就是未审核状态
            if paym.status != '0':
                return Response(data={'detail': '付款单已经审核，不需要再次审核'}, status=status.HTTP_400_BAD_REQUEST)

            #    判断2：是否关联了采购订单purchase_order，采购订单的状态是否为未审核
            #    引申：如果采购订单未审核，将无法创建付款单。
            if paym.purchase_order and paym.purchase_order.status == '0':
                return Response(data={'detail': '采购订单未审核，不能付款'}, status=status.HTTP_400_BAD_REQUEST)

            #    判断3：结算账户的余额是否充足
            if paym.this_money > paym.account.balance:
                return Response(data={'detail': '当前结算账户余额不足，无法付款'}, status=status.HTTP_400_BAD_REQUEST)

            # payment付款单的状态，0：未审核，1：已审核
            # purchase_in_storage采购入库单的状态，0：未审核，1：已审核，2：部分付款，3：付款完成（包含欠款）
            # purchase_order采购订单的状态/采购单的状态，0：未审核，1：已审核，2：部分入库，3：全部入库，4：已付定金，5：完成采购（货全部入库，付全款完成）
            # pay_category付款类型，1：采购货款，2：采购定金，3：尾款支付，4：其他付款

            # 二、处理关联业务逻辑
            #    1.业务一：支付采购定金
            #         pay_category付款类型：创建付款单的时候，选择付款类型
            if paym.pay_category == '2' and paym.purchase_order:
                paym.purchase_order.status = '4'  # 已付定金
                paym.purchase_order.save()

            #    2.业务二：支付尾款------除定金以外的款项都可以称之为尾款
            if paym.pay_category == '3':
                # pay_money：需要付款钱数
                # current_pay：期末应付(当前应付)，该字段来源于SupplierModel
                SupplierModel.objects.filter(id=paym.supplier.id).update(current_pay=F('current_pay') - paym.this_money)

            #   3.业务三：入库货款支付
            #           适用于：先入库再付款，根据采购入库单付款
            if paym.pay_category == '1' and paym.purchase_order:
                if paym.item_list.exists():  # 做这种判断，不需要调用数据库中的数据，节省对数据库的访问流量
                    # 加上.all()表示返回数据
                    for item in paym.item_list.all():
                        # item是付款单中的付款项目（采购入库单）

                        # 判断4：采购入库单必须是已审核状态
                        if item.purchase_in_storage and item.purchase_in_storage.status != '1':
                            return Response(data={'detail': '采购入库单未审核或者已经完成付款，无法进行付款'},
                                            status=status.HTTP_400_BAD_REQUEST)

                        # 业务三-----第一种情况：对应的“采购入库单”的状态是否要修改为：付款完成，首先要查询该采购入库单的已经付款金额之和
                        # purchase_in_storage采购入库单的状态，0：未审核，1：已审核，2：部分付款，3：付款完成（包含欠款）
                        sum_dict = PaymentItemModel.objects.filter(purchase_in_storage_id=item.purchase_in_storage.id,
                                                                   payment__status='1').aggregate(sum=Sum('this_money'))
                        pay_sum = sum_dict['sum'] if sum_dict['sum'] else 0
                        if item.should_money == (pay_sum + item.this_money):
                            item.purchase_in_storage.status = '3'
                            item.purchase_in_storage.save()

                        # 业务三-----第二种情况：对应的“采购订单”的状态是否要修改为：完成采购
                        # purchase采购订单的状态/采购单的状态，0：未审核，1：已审核，2：部分入库，3：全部入库，4：已付定金，5：完成采购（货全部入库，付全款完成）
                        # “完成采购”的条件：1.该采购订单的状态必须是：全部入库；2.该采购订单中所有采购入库单的状态也都是:付款完成
                        purchase = item.payment.purchase_order
                        if purchase.status == '3':
                            # 查询：该采购订单中所有的“采购入库单”的状态是否全部是“3付款完成”，或者说：是否还存在不是“3付款完成”的
                            # 最后直接调count()，不需要聚合函数aggregate()
                            number = InStorageModel.objects.filter(purchase_id=purchase.id).exclude(status='3').count()
                            if number == 0:  # 这时就是说所有的“采购入库单”全部都是“3付款完成”
                                purchase.status = '5'  # 修改“采购订单”的状态为：5完成采购
                                purchase.save()

                else:
                    return Response(data={'detail': '没有选择付款项目（采购入库单），无法付款，请选择对应的采购入库单'},
                                    status=status.HTTP_400_BAD_REQUEST)

            #   至此，前三种业务完成
            #    4.业务四：修改结算账户的余额
            SettlementAccountModel.objects.filter(id=paym.account.id).update(balance=F('balance') - paym.this_money)

        #   至此，审核前的验证工作完成
        #    进行审核操作
        self.get_queryset().filter(id__in=audit_ids).update(status="1", check_gen_user_name=check_user.real_name,
                                                            check_gen_user_id=check_user.id)
        return Response(status=status.HTTP_200_OK)
